<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Render Markdown</title>
  <script defer data-domain="tools.simonwillison.net" src="https://plausible.io/js/plausible.js"></script>
  <style>
    * {
      box-sizing: border-box;
    }

    body {
      padding: 1em;
      margin: 0;
      font-family: Helvetica, Arial, sans-serif;
      line-height: 1.5;
      max-width: 100%;
    }

    h1 {
      font-size: 1.8em;
      margin-top: 0.5em;
      margin-bottom: 0.5em;
    }

    p {
      margin: 0.75em 0;
    }

    textarea {
      width: 100%;
      font-size: 16px;
      padding: 0.75em;
      border: 1px solid #ccc;
      border-radius: 4px;
      margin-bottom: 1em;
      font-family: Helvetica, Arial, sans-serif;
    }

    #input {
      height: 40vh;
      min-height: 140px;
    }

    #output {
      height: 20vh;
      min-height: 80px;
      width: 100%;
      resize: vertical;
    }

    #preview {
      padding: 1em;
      border: 1px solid #ccc;
      border-radius: 4px;
      margin: 1em 0;
      overflow-x: auto;
    }

    button {
      font-size: 16px;
      padding: 0.75em 1.5em;
      background-color: #0366d6;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      margin-right: 0.5em;
      margin-bottom: 0.5em;
      touch-action: manipulation;
    }

    button:hover {
      background-color: #0253b3;
    }

    #copy-button {
      position: relative;
      margin: 0 0 0.5em 0;
      height: 40px;
      min-width: 120px;
      background-color: #2ea44f;
      border-radius: 4px;
      border: none;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    #copy-button:active {
      background-color: #22863a;
    }

    .copy-text {
      font-size: 14px;
    }

    .controls {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 0.75em;
      margin-bottom: 1em;
    }

    .output-container {
      position: relative;
      margin: 0.5em 0 1.5em 0;
    }

    label {
      display: flex;
      align-items: center;
      gap: 0.5em;
      margin-right: 1em;
      font-size: 0.9em;
    }

    input[type="checkbox"] {
      width: 1.2em;
      height: 1.2em;
    }

    h3 {
      margin: 1.5em 0 0.5em 0;
      font-size: 1.2em;
    }

    /* Message after successful copy */
    .copied-message {
      position: absolute;
      background-color: rgba(0, 0, 0, 0.7);
      color: white;
      padding: 4px 8px;
      border-radius: 4px;
      font-size: 14px;
      top: -30px;
      left: 50%;
      transform: translateX(-50%);
      opacity: 0;
      transition: opacity 0.3s;
      pointer-events: none;
    }

    .show-message {
      opacity: 1;
    }

    @media (max-width: 768px) {
      h1 {
        font-size: 1.5em;
      }

      textarea {
        font-size: 16px;
      }

      button {
        width: 100%;
        margin-right: 0;
      }

      .controls {
        flex-direction: column;
        align-items: flex-start;
      }

      label {
        margin-bottom: 0.5em;
      }
    }

    /* TOC styling */
    .toc {
      background-color: #f6f8fa;
      padding: 1em;
      border-radius: 4px;
      margin-bottom: 1em;
    }

    .toc h2 {
      margin-top: 0;
      font-size: 1.2em;
    }

    .toc ul {
      padding-left: 1.5em;
      margin: 0.5em 0;
    }

    /* Additional mobile styles for content */
    #preview img {
      max-width: 100%;
      height: auto;
    }

    #preview pre {
      overflow-x: auto;
      white-space: pre-wrap;
      word-wrap: break-word;
    }

    #preview table {
      display: block;
      width: 100%;
      overflow-x: auto;
      border-collapse: collapse;
    }

    #preview blockquote {
      border-left: 4px solid #dfe2e5;
      padding-left: 1em;
      color: #586069;
      margin-left: 0;
    }

    code {
      font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
      font-size: 85%;
      background-color: rgba(27, 31, 35, 0.05);
      padding: 0.2em 0.4em;
      border-radius: 3px;
    }

    pre > code {
      padding: 0;
      background-color: transparent;
    }

    /* GitHub Light v0.5.0 CSS */
    .pl-c {
      color: #6a737d;
    }

    .pl-c1,
    .pl-s .pl-v {
      color: #005cc5;
    }

    .pl-e,
    .pl-en {
      color: #6f42c1;
    }

    .pl-smi,
    .pl-s .pl-s1 {
      color: #24292e;
    }

    .pl-ent {
      color: #22863a;
    }

    .pl-k {
      color: #d73a49;
    }

    .pl-s,
    .pl-pds,
    .pl-s .pl-pse .pl-s1,
    .pl-sr,
    .pl-sr .pl-cce,
    .pl-sr .pl-sre,
    .pl-sr .pl-sra {
      color: #032f62;
    }

    .pl-v,
    .pl-smw {
      color: #e36209;
    }

    .pl-bu {
      color: #b31d28;
    }

    .pl-ii {
      color: #fafbfc;
      background-color: #b31d28;
    }

    .pl-c2 {
      color: #fafbfc;
      background-color: #d73a49;
    }

    .pl-c2::before {
      content: "^M";
    }

    .pl-sr .pl-cce {
      font-weight: bold;
      color: #22863a;
    }

    .pl-ml {
      color: #735c0f;
    }

    .pl-mh,
    .pl-mh .pl-en,
    .pl-ms {
      font-weight: bold;
      color: #005cc5;
    }

    .pl-mi {
      font-style: italic;
      color: #24292e;
    }

    .pl-mb {
      font-weight: bold;
      color: #24292e;
    }

    .pl-md {
      color: #b31d28;
      background-color: #ffeef0;
    }

    .pl-mi1 {
      color: #22863a;
      background-color: #f0fff4;
    }

    .pl-mc {
      color: #e36209;
      background-color: #ffebda;
    }

    .pl-mi2 {
      color: #f6f8fa;
      background-color: #005cc5;
    }

    .pl-mdr {
      font-weight: bold;
      color: #6f42c1;
    }

    .pl-ba {
      color: #586069;
    }

    .pl-sg {
      color: #959da5;
    }

    .pl-corl {
      text-decoration: underline;
      color: #032f62;
    }
  </style>
</head>
<body>
  <h1>Render Markdown</h1>
  <p>Using GitHub's API, see <a href="https://til.simonwillison.net/markdown/github-markdown-api">Rendering Markdown with the GitHub Markdown API</a></p>
  
  <textarea id="input" placeholder="Enter your markdown here..."></textarea>
  
  <div class="controls">
    <button id="render-button">Render</button>
    
    <label>
      <input type="checkbox" id="strip_hidden" checked> 
      Strip hidden and cleanup HTML
    </label>
    
    <label>
      <input type="checkbox" id="gfm_mode"> 
      Use GitHub Flavored Markdown (GFM)
    </label>
  </div>
  
  <div id="preview"></div>
  
  <h3>HTML Output</h3>
  <button id="copy-button" aria-label="Copy HTML to clipboard">
    <span class="copy-text">Copy HTML</span>
  </button>
  
  <div class="output-container">
    <textarea id="output" readonly></textarea>
  </div>
  
  <script type="module">
// Initialize elements
const button = document.getElementById('render-button');
const stripHidden = document.getElementById('strip_hidden');
const input = document.getElementById('input');
const output = document.getElementById('output');
const preview = document.getElementById('preview');

// Add render functionality
button.addEventListener('click', async function() {
  try {
    button.textContent = 'Rendering...';
    button.disabled = true;
    
    const rendered = await render(input.value);
    output.value = rendered;
    preview.innerHTML = rendered;
    
    if (stripHidden.checked) {
      cleanupHTML();
    }
    
    // Generate and add TOC
    generateTOC();
    
    button.textContent = 'Render';
    button.disabled = false;
  } catch (error) {
    console.error('Error rendering markdown:', error);
    preview.innerHTML = `<div style="color: red;">Error rendering markdown: ${error.message}</div>`;
    button.textContent = 'Render';
    button.disabled = false;
  }
});

// Function to cleanup HTML
function cleanupHTML() {
  // Remove aria-hidden elements
  Array.from(
    preview.querySelectorAll('[aria-hidden]')
  ).forEach(el => el.parentNode.removeChild(el));
  
  // Remove nofollow attributes
  Array.from(
    preview.querySelectorAll('[rel="nofollow"]')
  ).forEach(el => el.removeAttribute('rel'));
  
  // Remove Python highlight div wrappers
  Array.from(
    preview.querySelectorAll('div.highlight-source-python')
  ).forEach(el => el.replaceWith(el.firstChild));
  
  // Fix image links
  Array.from(
    preview.querySelectorAll('a[href^="https://camo.githubusercontent.com"]')
  ).forEach(el => el.replaceWith(el.firstChild));
  
  Array.from(
    preview.querySelectorAll('img[data-canonical-src]')
  ).forEach(el => {
    el.setAttribute('src', el.getAttribute('data-canonical-src'));
    el.removeAttribute('data-canonical-src');
  });
  
  // Remove heading links
  Array.from(
    preview.querySelectorAll('a.heading-link')
  ).forEach(el => el.replaceWith(el.firstChild));
  
  // Clean up markdown-heading elements
  Array.from(
    preview.querySelectorAll('div.markdown-heading')
  ).forEach(el => {
    const heading = el.querySelector('.heading-element');
    if (heading) {
      const id = heading.innerText.toLowerCase().replace(/[^a-z0-9]+/g, '-');
      const tag = heading.tagName;
      heading.setAttribute('id', id);
      heading.removeAttribute('class');
      el.parentNode.replaceChild(heading, el);
    }
  });
  
  // Format output for textarea
  output.value = preview.innerHTML
    .replace(/<h([1-6])>\n/g, '<h$1>')
    .replace(/<br>/g, '<br />')
    .replace(/<img ([^>].+?)>/g, '<img $1 />');
}

// Function to generate table of contents
function generateTOC() {
  const headings = Array.from(
    preview.querySelectorAll('h2[id],h3[id],h4[id],h5[id],h6[id]')
  );
  
  if (headings.length > 0) {
    const tocDiv = document.createElement('div');
    tocDiv.className = 'toc';
    tocDiv.innerHTML = '<h2>Contents</h2>';
    
    const tocList = document.createElement('ul');
    headings.forEach(el => {
      const li = document.createElement('li');
      const a = document.createElement('a');
      a.href = `#${el.id}`;
      a.textContent = el.innerText;
      li.appendChild(a);
      tocList.appendChild(li);
    });
    
    tocDiv.appendChild(tocList);
    preview.insertBefore(tocDiv, preview.firstChild);
  }
}

// Function to render markdown using GitHub API
async function render(markdown) {
  const mode = document.getElementById('gfm_mode').checked ? 'gfm' : 'markdown';
  try {
    const response = await fetch('https://api.github.com/markdown', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ 'mode': mode, 'text': markdown })
    });
    
    if (!response.ok) {
      throw new Error(`GitHub API returned ${response.status}: ${await response.text()}`);
    }
    
    return await response.text();
  } catch (error) {
    console.error('Error calling GitHub API:', error);
    throw error;
  }
}

// Add copy to clipboard functionality
const copyButton = document.getElementById('copy-button');

function showCopySuccess() {
  const textSpan = copyButton.querySelector('.copy-text');
  const originalText = textSpan.textContent;
  textSpan.textContent = '✓ Copied!';
  copyButton.style.backgroundColor = '#22863a';
  setTimeout(() => {
    textSpan.textContent = originalText;
    copyButton.style.backgroundColor = '';
  }, 2000);
}

function copyWithExecCommand() {
  // Focus the textarea and select all content
  output.focus();
  output.select();
  output.setSelectionRange(0, output.value.length);

  try {
    const successful = document.execCommand('copy');
    if (successful) {
      showCopySuccess();
      return true;
    }
  } catch (err) {
    console.error('execCommand copy failed:', err);
  }
  return false;
}

copyButton.addEventListener('click', async () => {
  if (!output.value) {
    return;
  }

  // Try modern clipboard API first
  if (navigator.clipboard && navigator.clipboard.writeText) {
    try {
      await navigator.clipboard.writeText(output.value);
      showCopySuccess();
      return;
    } catch (err) {
      console.warn('navigator.clipboard.writeText failed, trying fallback:', err);
    }
  }

  // Fallback to execCommand
  if (!copyWithExecCommand()) {
    // Last resort: prompt user to copy manually
    output.focus();
    output.select();
    alert('Press Ctrl+C (Cmd+C on Mac) to copy the selected text.');
  }
});

// Populate textarea from localStorage and set up saving
if (!input.value && localStorage.getItem('saved')) {
  input.value = localStorage.getItem('saved');
  button.click();
}

input.addEventListener('input', () => {
  localStorage.setItem('saved', input.value);
});

// Add double-tap zoom prevention
document.addEventListener('touchstart', function(event) {
  if (event.target.tagName === 'BUTTON' || event.target.tagName === 'INPUT') {
    event.preventDefault();
    event.target.click();
  }
}, { passive: false });

// Add service worker for offline capabilities if needed
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js').catch(err => {
      console.log('Service Worker registration failed: ', err);
    });
  });
}
  </script>
</body>
</html>
